/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.component.schedule.core.job;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.pattern.CronPattern;
import cn.hutool.cron.pattern.CronPatternUtil;
import com.google.common.collect.Lists;
import info.peigen.hotpot.common.core.enums.Symbol;
import info.peigen.hotpot.common.core.enums.YesOrNo;
import info.peigen.hotpot.component.schedule.core.domain.ScheduleJob;
import info.peigen.hotpot.component.schedule.core.domain.action.ScheduleJobConvertor;
import info.peigen.hotpot.component.schedule.core.repository.entity.ScheduleJobEntity;
import info.peigen.hotpot.component.schedule.core.repository.mapper.ScheduleJobMapper;
import info.peigen.hotpot.component.schedule.facade.job.ScheduleFacadeJob;
import lombok.extern.slf4j.Slf4j;
import org.beetl.sql.solon.annotation.Db;
import org.noear.solon.annotation.Component;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static info.peigen.hotpot.component.schedule.facade.enums.ScheduleExecuteResultCode.UNKNOWN;

/**
 * <b>(ScheduleExecute)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2022/12/30
 */
@Component
@Slf4j
public class ScheduleExecute {
    @Db
    private ScheduleJobMapper scheduleJobMapper;

    public void execute(List<ScheduleFacadeJob> beans) {
        log.debug("当前需要执行的Job有{}", CollUtil.join(beans, Symbol.COMMA.code()));

        // TODO Peigen 这里可以优化一下事务，拆分成单个锁
        List<ScheduleJobEntity> scheduleJobEntities = scheduleJobMapper.lockByEnable(YesOrNo.Y);

        // 更新未加入数据库的新定时任务
        updateJobs(beans, scheduleJobEntities);

        // 执行已到执行时间的定时任务
        executeJob(beans, scheduleJobEntities);
    }

    /**
     * 更新未加入数据库的新定时任务
     *
     * @param beans               定时任务bean全集
     * @param scheduleJobEntities 已加入数据库的定时任务
     */
    private void updateJobs(List<ScheduleFacadeJob> beans, List<ScheduleJobEntity> scheduleJobEntities) {
        List<String>            entityNames        = scheduleJobEntities.stream().map(ScheduleJobEntity::getJobName).collect(Collectors.toList());
        List<ScheduleFacadeJob> scheduleFacadeJobs = Lists.newArrayList();
        beans.stream()
                .filter(bean -> !CollUtil.contains(entityNames, bean.jobName()))
                .findAny()
                // 填充未记录数据库的新的定时任务
                .ifPresent(scheduleFacadeJobs::add);

        List<ScheduleJob> scheduleJobs = scheduleFacadeJobs.stream().map(job -> ScheduleJob.builder()
                .jobName(job.jobName())
                .cron(job.cron().build())
                .communicationMethod(job.method())
                .url(job.urlOrEndPoint())
                .endPoint(job.urlOrEndPoint())
                .preExecuteTime(null)
                .preExecuteResultCode(UNKNOWN)
                .nextExecuteTime(genNextExecuteTime(job))
                .executeTimeout(job.executeTimeout())
                .executeFailRetryCount(job.executeFailRetryCount())
                .build()).collect(Collectors.toList());

        List<ScheduleJobEntity> scheduleJobEntityList = scheduleJobs.stream().map(ScheduleJobConvertor::convert).collect(Collectors.toList());
        scheduleJobMapper.insertBatch(scheduleJobEntityList);
    }

    private DateTime genNextExecuteTime(ScheduleFacadeJob job) {
        CronPattern cronPattern = CronPattern.of(job.cron().build());
        Date        date        = CronPatternUtil.nextDateAfter(cronPattern, DateUtil.date(), true);
        return new DateTime(date);
    }

    /**
     * 执行已到执行时间的定时任务
     *
     * @param beans               定时任务bean全集
     * @param scheduleJobEntities 已加入数据库的定时任务
     */
    private void executeJob(List<ScheduleFacadeJob> beans, List<ScheduleJobEntity> scheduleJobEntities) {
        scheduleJobEntities.stream().filter(entity -> DateUtil.compare(DateUtil.date(), entity.getNextExecuteTime()) >= 0)
                .forEach(entity -> {
                    switch (entity.getCommunicationMethod()) {
                        case Http:
                        case Https:
                        case Socket:
                        case WebService:
                            httpExecute(entity, beans);
                            break;
                        case Bean:
                            beanExecute(entity, beans);
                            break;
                        default:
                            break;
                    }
                });
    }

    private void httpExecute(ScheduleJobEntity entity, List<ScheduleFacadeJob> beans) {
        log.warn("Peigen偷懒，整体未实现");
    }

    private void beanExecute(ScheduleJobEntity entity, List<ScheduleFacadeJob> beans) {
        beans.stream()
                .filter(bean -> StrUtil.equals(bean.jobName(), entity.getJobName()))
                .forEach(ScheduleFacadeJob::execute);
    }
}